package org.luxor.commons.core.utils;

import org.luxor.commons.core.exception.BaseException;
import org.luxor.commons.core.web.RestStatus;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * Http 工具类
 *
 * @author XinmingYan @date 2019/9/16 23:09
 */
public class HttpContextUtils extends org.springframework.web.util.WebUtils {
    private static final String BASIC_ = "Basic ";
    private static final String UNKNOWN = "unknown";

    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static HttpServletResponse getHttpServletResponse() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    }

    /**
     * 读取cookie
     */
    public static String getCookieVal(String name) {
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        Assert.notNull(request, "request from RequestContextHolder is null");

        Cookie cookie = getCookie(request, name);
        return cookie != null ? cookie.getValue() : null;
    }

    /**
     * 清除 某个指定的cookie
     */
    public static void removeCookie(HttpServletResponse response, String key) {
        setCookie(response, key, null, 0);
    }

    /**
     * 设置cookie
     */
    public static void setCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds) {
        Cookie cookie = new Cookie(name, value);
        cookie.setPath("/");
        cookie.setMaxAge(maxAgeInSeconds);
        cookie.setHttpOnly(true);
        response.addCookie(cookie);
    }

    /**
     * 返回json
     */
    public static void sendJson(HttpServletResponse response, Object result) {
        Assert.notNull(response, "response from RequestContextHolder is null");
        response.setCharacterEncoding("UTF-8");
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter out = null;
        try {
            out = response.getWriter();
            out.append(Jackson2Utils.toJson(result));
            out.flush();
        } catch (IOException e) {
            throw new BaseException("Try IOException on response.getWriter ", e);
        } finally {
            IoUtils.closeQuietly(out);
        }
    }

    /**
     * 返回字节流
     *
     * @param fileByte
     * @param response
     */
    public static void write(byte[] fileByte, HttpServletResponse response) {
        ServletOutputStream outputStream = null;
        try {
            response.setIntHeader("Content-length", fileByte.length);
            outputStream = response.getOutputStream();
            outputStream.write(fileByte);
            outputStream.flush();
        } catch (IOException e) {
            throw new BaseException("HttpServletResponse Write Fault!", e);
        } finally {
            IoUtils.closeQuietly(outputStream);
        }
    }

    /**
     * @return http://localhost:8080
     */
    public static String getDomain(HttpServletRequest request) {
        StringBuffer url = request.getRequestURL();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
    }

    /**
     * @return https://www.baidu.com
     */
    public static String getOrigin(HttpServletRequest request) {
        return request.getHeader("Origin");
    }

    public static boolean isAjax(HttpServletRequest request) {
        String header = request.getHeader("x-requested-with");

        if (header == null || header.length() == 0 || "unknown".equalsIgnoreCase(header)) {
            return false;
        }

        if ("XMLHttpRequest".equals(header)) {
            return true;
        }
        return false;
    }

    /**
     * 14.215.177.39
     */
    public static String getRemoteAddr(HttpServletRequest request) {
        Assert.notNull(request, "HttpServletRequest is null");
        String ip = request.getHeader("X-Requested-For");
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return StringUtils.isBlank(ip) ? null : ip.split(",")[0];
    }

    public static String[] getClientId() {
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        Assert.notNull(request, "request from RequestContextHolder is null");

        String header = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (header == null || !header.startsWith(BASIC_)) {
            throw new BaseException(RestStatus.TOKEN_INVALID, "请求头中client信息为空");
        }

        byte[] decoded;
        try {
            byte[] base64Token = header.substring(6).getBytes(StrKit.CHARSET_NAME);
            decoded = Base64.getDecoder().decode(base64Token);
        } catch (IllegalArgumentException | UnsupportedEncodingException e) {
            throw new BaseException(RestStatus.TOKEN_INVALID, "Failed to decode basic authentication token");
        }

        String token = new String(decoded, StandardCharsets.UTF_8);
        int delim = token.indexOf(":");
        if (delim == -1) {
            throw new BaseException(RestStatus.TOKEN_INVALID, "Invalid basic authentication token");
        }
        return new String[]{token.substring(0, delim), token.substring(delim + 1)};
    }
}
